// Check the DB Node Config and privilege is right.
package main

import (
	"encoding/json"
	"epg/utils"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/hpcloud/tail"

	"strconv"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/parsers/influx"
	"github.com/urfave/cli"
)

//consts
const (
	App    = "epg"
	SP1    = "\u0001"
	SP2    = "\u0002"
	SP3    = "\u0003"
	SP4    = "\u0004"
	Format = "OWL${SP1}STATS${SP1}0002${SP1}${APP}${SP1}%d${SP1}%s${SP1}slowquery${SP3}usetime${SP1}1${SP3}%s${SP3}%s${SP4}\n"
)

var sparr = map[string]string{
	"${APP}": App,
	"${SP1}": SP1,
	"${SP2}": SP2,
	"${SP3}": SP3,
	"${SP4}": SP4,
}

//OWL owl format logger
type OWL struct {
	in     *tail.Tail
	out    *os.File
	parser *influx.InfluxParser
	tmpl   string
}

func main() {
	app := cli.NewApp()
	app.Name = "Epg Proxy OWL Logger"
	app.Usage = "Parse the epg service slowquery log to owl format."
	app.Version = "0.0.1"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name: "input,i", Value: "slowquery.log", Usage: "Input slow query log file",
		},
		cli.StringFlag{
			Name: "output,o", Value: "slowquery_owl.log", Usage: "Output slow query log file",
		},
		cli.BoolFlag{
			Name: "frombegin,b", Usage: "Read the slow query log file from begin.",
		},
	}
	app.Action = handle
	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
}

func handle(ctx *cli.Context) error {
	var seek = &tail.SeekInfo{} //frombegin
	if !ctx.Bool("b") {
		//from last
		seek = &tail.SeekInfo{
			Offset: 0,
			Whence: 2,
		}
	}
	in, err := tail.TailFile(ctx.String("i"), tail.Config{
		ReOpen:    true,
		Follow:    true,
		Location:  seek,
		MustExist: true,
	})
	if err != nil {
		return err
	}
	out, err := os.OpenFile(ctx.String("o"), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
	if err != nil {
		return err
	}
	var tmpl = Format
	for o, n := range sparr {
		tmpl = strings.Replace(tmpl, o, n, -1)
	}
	owl := &OWL{
		in:     in,
		out:    out,
		parser: &influx.InfluxParser{},
		tmpl:   tmpl,
	}
	return owl.readInout()
}

func (owl *OWL) readInout() error {
	defer utils.PrintPanicStack()

	var err error
	var line *tail.Line
	for line = range owl.in.Lines {
		if line.Err != nil {
			log.Printf("ERROR tailing file %s, Error: %s\n", owl.in.Filename, err)
			continue
		}
		err = owl.writeOutput(line)
		if err != nil {
			log.Printf("Malformed log line in %s: [%s], Error: %s\n", owl.in.Filename, line.Text, err)
			break
		}
	}
	return err
}

func (owl *OWL) writeOutput(line *tail.Line) error {
	metric, err := owl.parser.ParseLine(line.Text)
	if err != nil {
		return err
	}
	out := owl.format(metric)
	_, err = owl.out.WriteString(out)
	if err != nil {
		return err
	}
	return nil
}

func (owl *OWL) format(m telegraf.Metric) string {
	var str = owl.tmpl
	//created, ip,v2,v3
	var usetime string
	t, ok := m.Fields()["usetime"].(float64)
	if !ok {
		t = 0
	}
	usetime = fmt.Sprintf("%.3f", t/1e9)
	node := strings.Split(m.Tags()["node"], ":")
	tags := m.Tags()
	sql, ok := m.Fields()["sql"].(string)
	if !ok {
		sql = ""
	}
	_sql, err := strconv.Unquote(`"` + sql + `"`)
	if err != nil {
		log.Println("strconv.Unquote(): ", err, sql)
	}
	js := map[string]interface{}{
		"epg_host":  tags["host"],
		"user":      tags["user"],
		"db":        tags["db"],
		"usetime":   usetime,
		"db_ip":     node[0],
		"db_port":   node[1],
		"client_ip": tags["ip"],
		"sql":       _sql,
	}
	v3, err := json.Marshal(js)
	if err != nil {
		return ""
	}
	str = fmt.Sprintf(str, m.Time().Unix(), tags["ip"], usetime, v3)
	return str
}
